using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections;
using System.Windows.Forms;
using System.Xml;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
using DesignSurfaceExt;
namespace Loader
{
   public  class Savers
    {
       private XmlDocument xmlDocument;
       private static readonly Attribute[] propertyAttributes = new Attribute[] {
			DesignOnlyAttribute.No
		};
       public Savers()
       {

       }
       public void SavePrj(IDesignSurfaceExt isurf,string sPath)
       {
           XmlDocument document = new XmlDocument();
           document.AppendChild(document.CreateElement("DOCUMENT_ELEMENT"));

           IDesignerHost idh = (IDesignerHost)isurf.GetIDesignerHost().GetService(typeof(IDesignerHost));
            IComponent root = idh.RootComponent;

           Hashtable nametable = new Hashtable(idh.Container.Components.Count);
           IDesignerSerializationManager manager = isurf.GetIDesignerHost().GetService(typeof(IDesignerSerializationManager)) as IDesignerSerializationManager;

           document.DocumentElement.AppendChild(WriteObject(isurf,document, nametable, root));
           foreach (IComponent comp in idh.Container.Components)
           {
               if (comp != root && !nametable.ContainsKey(comp))
               {
                   document.DocumentElement.AppendChild(WriteObject(isurf,document, nametable, comp));
               }
           }

           xmlDocument = document;
           // Write out our xmlDocument to a file.
           StringWriter sw = new StringWriter();
           XmlTextWriter xtw = new XmlTextWriter(sw);
           xtw.Formatting = Formatting.Indented;
           xmlDocument.WriteTo(xtw);
           // Get rid of our artificial super-root before we save out
           // the XML.
           string cleanup = sw.ToString().Replace("<DOCUMENT_ELEMENT>", "");
           cleanup = cleanup.Replace("</DOCUMENT_ELEMENT>", "");
           xtw.Close();
           StreamWriter file = new StreamWriter(sPath);
           file.Write(cleanup);
           file.Close();
       }


       private XmlNode WriteObject(IDesignSurfaceExt isurf,XmlDocument document, IDictionary nametable, object value)
       {
           IDesignerHost idh = (IDesignerHost)isurf.GetIDesignerHost().GetService(typeof(IDesignerHost));
           Debug.Assert(value != null, "Should not invoke WriteObject with a null value");

           XmlNode node = document.CreateElement("Object");
           XmlAttribute typeAttr = document.CreateAttribute("type");

           typeAttr.Value = value.GetType().AssemblyQualifiedName;
           node.Attributes.Append(typeAttr);

           IComponent component = value as IComponent;

           if (component != null && component.Site != null && component.Site.Name != null)
           {
               XmlAttribute nameAttr = document.CreateAttribute("name");

               nameAttr.Value = component.Site.Name;
               node.Attributes.Append(nameAttr);
               Debug.Assert(nametable[component] == null, "WriteObject should not be called more than once for the same object.  Use WriteReference instead");
               nametable[value] = component.Site.Name;
           }

           bool isControl = (value is Control);

           if (isControl)
           {
               XmlAttribute childAttr = document.CreateAttribute("children");

               childAttr.Value = "Controls";
               node.Attributes.Append(childAttr);
           }

           if (component != null)
           {
               if (isControl)
               {
                   foreach (Control child in ((Control)value).Controls)
                   {
                       if (child.Site != null && child.Site.Container == idh.Container)
                       {
                           node.AppendChild(WriteObject(isurf,document, nametable, child));
                       }
                   }
               }// if isControl

               PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value, propertyAttributes);

               if (isControl)
               {
                   PropertyDescriptor controlProp = properties["Controls"];

                   if (controlProp != null)
                   {
                       PropertyDescriptor[] propArray = new PropertyDescriptor[properties.Count - 1];
                       int idx = 0;

                       foreach (PropertyDescriptor p in properties)
                       {
                           if (p != controlProp)
                           {
                               propArray[idx++] = p;
                           }
                       }

                       properties = new PropertyDescriptorCollection(propArray);
                   }
               }

               WriteProperties( isurf,document, properties, value, node, "Property");

               EventDescriptorCollection events = TypeDescriptor.GetEvents(value, propertyAttributes);
               IEventBindingService bindings = isurf.GetIDesignerHost().GetService(typeof(IEventBindingService)) as IEventBindingService;

               if (bindings != null)
               {
                   properties = bindings.GetEventProperties(events);
                   WriteProperties(isurf,document, properties, value, node, "Event");
               }
           }
           else
           {
               WriteValue(isurf,document, value, node);
           }

           return node;
       }

       private void WriteProperties(IDesignSurfaceExt isurf,XmlDocument document, PropertyDescriptorCollection properties, object value, XmlNode parent, string elementName)
       {
           foreach (PropertyDescriptor prop in properties)
           {
               if (prop.Name == "AutoScaleBaseSize")
               {
                   string _DEBUG_ = prop.Name;
               }

               if (prop.ShouldSerializeValue(value))
               {
                   string compName = parent.Name;
                   XmlNode node = document.CreateElement(elementName);
                   XmlAttribute attr = document.CreateAttribute("name");

                   attr.Value = prop.Name;
                   node.Attributes.Append(attr);

                   DesignerSerializationVisibilityAttribute visibility = (DesignerSerializationVisibilityAttribute)prop.Attributes[typeof(DesignerSerializationVisibilityAttribute)];

                   switch (visibility.Visibility)
                   {
                       case DesignerSerializationVisibility.Visible:
                           if (!prop.IsReadOnly && WriteValue(isurf,document, prop.GetValue(value), node))
                           {
                               parent.AppendChild(node);
                           }

                           break;

                       case DesignerSerializationVisibility.Content:
                           object propValue = prop.GetValue(value);

                           if (typeof(IList).IsAssignableFrom(prop.PropertyType))
                           {
                               WriteCollection(isurf,document, (IList)propValue, node);
                           }
                           else
                           {
                               PropertyDescriptorCollection props = TypeDescriptor.GetProperties(propValue, propertyAttributes);

                               WriteProperties(isurf,document, props, propValue, node, elementName);
                           }

                           if (node.ChildNodes.Count > 0)
                           {
                               parent.AppendChild(node);
                           }

                           break;

                       default:
                           break;
                   }
               }
           }
       }
       private void WriteCollection(IDesignSurfaceExt isurf, XmlDocument document, IList list, XmlNode parent)
       {
           foreach (object obj in list)
           {
               XmlNode node = document.CreateElement("Item");
               XmlAttribute typeAttr = document.CreateAttribute("type");

               typeAttr.Value = obj.GetType().AssemblyQualifiedName;
               node.Attributes.Append(typeAttr);
               WriteValue(isurf,document, obj, node);
               parent.AppendChild(node);
           }
       }
       private XmlNode WriteBinary(XmlDocument document, byte[] value)
       {
           XmlNode node = document.CreateElement("Binary");

           node.InnerText = Convert.ToBase64String(value);
           return node;
       }
       private bool GetConversionSupported(TypeConverter converter, Type conversionType)
       {
           return (converter.CanConvertFrom(conversionType) && converter.CanConvertTo(conversionType));
       }
       private XmlNode WriteInstanceDescriptor(IDesignSurfaceExt isurf, XmlDocument document, InstanceDescriptor desc, object value)
       {
           XmlNode node = document.CreateElement("InstanceDescriptor");
           BinaryFormatter formatter = new BinaryFormatter();
           MemoryStream stream = new MemoryStream();

           formatter.Serialize(stream, desc.MemberInfo);

           XmlAttribute memberAttr = document.CreateAttribute("member");

           memberAttr.Value = Convert.ToBase64String(stream.ToArray());
           node.Attributes.Append(memberAttr);
           foreach (object arg in desc.Arguments)
           {
               XmlNode argNode = document.CreateElement("Argument");

               if (WriteValue(isurf,document, arg, argNode))
               {
                   node.AppendChild(argNode);
               }
           }

           if (!desc.IsComplete)
           {
               PropertyDescriptorCollection props = TypeDescriptor.GetProperties(value, propertyAttributes);

               WriteProperties(isurf,document, props, value, node, "Property");
           }

           return node;
       }
       private XmlNode WriteReference(IDesignSurfaceExt isurf, XmlDocument document, IComponent value)
       {
           IDesignerHost idh = (IDesignerHost)isurf.GetIDesignerHost().GetService(typeof(IDesignerHost));

           Debug.Assert(value != null && value.Site != null && value.Site.Container == idh.Container, "Invalid component passed to WriteReference");

           XmlNode node = document.CreateElement("Reference");
           XmlAttribute attr = document.CreateAttribute("name");

           attr.Value = value.Site.Name;
           node.Attributes.Append(attr);
           return node;
       }
       private bool WriteValue(IDesignSurfaceExt isurf,XmlDocument document, object value, XmlNode parent)
       {
           IDesignerHost idh = (IDesignerHost)isurf.GetIDesignerHost().GetService(typeof(IDesignerHost));

           // For empty values, we just return.  This creates an empty node.
           if (value == null)
           {
               return true;
           }

           TypeConverter converter = TypeDescriptor.GetConverter(value);

           if (GetConversionSupported(converter, typeof(string)))
           {
               parent.InnerText = (string)converter.ConvertTo(null, CultureInfo.InvariantCulture, value, typeof(string));
           }
           else if (GetConversionSupported(converter, typeof(byte[])))
           {
               byte[] data = (byte[])converter.ConvertTo(null, CultureInfo.InvariantCulture, value, typeof(byte[]));

               parent.AppendChild(WriteBinary(document, data));
           }
           else if (GetConversionSupported(converter, typeof(InstanceDescriptor)))
           {
               InstanceDescriptor id = (InstanceDescriptor)converter.ConvertTo(null, CultureInfo.InvariantCulture, value, typeof(InstanceDescriptor));

               parent.AppendChild(WriteInstanceDescriptor(isurf,document, id, value));
           }
           else if (value is IComponent && ((IComponent)value).Site != null && ((IComponent)value).Site.Container == idh.Container)
           {
               parent.AppendChild(WriteReference(isurf,document, (IComponent)value));
           }
           else if (value.GetType().IsSerializable)
           {
               BinaryFormatter formatter = new BinaryFormatter();
               MemoryStream stream = new MemoryStream();

               formatter.Serialize(stream, value);

               XmlNode binaryNode = WriteBinary(document, stream.ToArray());

               parent.AppendChild(binaryNode);
           }
           else
           {
               return false;
           }

           return true;
       }





    }
}
